/*
 * MS5541.c
 *
 * Created: 10.01.2014 16:40:43
 *  Author: Hf
 */ 

//#define F_CPU			4190000UL	// Diese Definition ist fuer <util/delay.h> notwendig
#include "defines.h"
#include <stdio.h>
#include <avr/io.h>
//#include <util/delay.h>

#include "MS5541.h"

#define SPI_DDR DDRB	// DDR of SPI port
#define SPI_PORT PORTB	// SPI port
#define SPI_MOSI PB5	// MOSI pin (Master out, Slave in)
#define SPI_MISO PB6	// MISO pin (Master in, Slave out)
#define SPI_SCK PB7		// SCK pin (SPI clock)
#define SPI_SS PB4		// SS pin (Slave Select)

// wait for an SPI read/write operation to complete
#define SPI_WAIT() while ((SPSR & _BV(SPIF)) == 0);

// bit patterns for reading calib words and sensor data, and for sending reset
#define READ_W1 0b0001110101010000
#define READ_W2 0b0001110101100000
#define READ_W3 0b0001110110010000
#define READ_W4 0b0001110110100000
//#define READ_W_BITS 12

#define READ_D1 0b0000111101000000	// pressure
#define READ_D2 0b0000111100100000	// temperature
//#define READ_D_BITS 10

#define RESET   0b1010101010101010
///#define RESET_BITS 21

void send_bytes(uint16_t data)
{
	SPCR &= ~_BV(CPOL);
	SPDR = data >> 8;
	SPI_WAIT();
	SPDR = data & 0xFF;
	SPI_WAIT();
}

uint16_t get_bytes()
{
	uint16_t data;
	SPCR |= _BV(CPOL);
	SPDR = 0;
	SPI_WAIT();
	data = SPDR;
	data <<= 8;
	SPDR = 0;
	SPI_WAIT();
	data |= SPDR;
	return data;
}


void get_coeffs(coeff_t* coefficients)
{
	uint16_t w1, w2, w3, w4;
	
	send_bytes(READ_W1);
	w1 = get_bytes();
	send_bytes(READ_W2);
	w2 = get_bytes();
	send_bytes(READ_W3);
	w3 = get_bytes();
	send_bytes(READ_W4);
	w4 = get_bytes();
	
	coefficients->SENST1 = w1 >> 3;
	
	coefficients->OFFT1 = (w1 & 0b111) << 10;
	coefficients->OFFT1 |= w2 >> 6;
	
	coefficients->TCS = w3 >> 6;
	
	coefficients->TCO = w4 >> 7;
	
	coefficients->Tref = (w2 & 0b111111) << 6;
	coefficients->Tref |= w3 & 0b111111;
	
	coefficients->TEMPSENS = w4 & 0b1111111;
}

uint16_t read_temp()
{
	send_bytes(READ_D2);
	//send_bytes(0);
	//while (MISO_HIGH());	// MISO goes low on conversion completion
	delay_ms(35);
	return get_bytes();
}

int16_t get_temp_diff(coeff_t* coeffs)
{
	uint16_t T = read_temp();
	// find reference temperature
	uint16_t UT1 = 8 * coeffs->Tref + 10000;	// maximum value is 42760
	int16_t dT = T - UT1;		// find difference between temperature reading and reference
	// calculate second-order temperature differential
	int32_t dT_sq = dT;
	dT_sq *= dT;	// dT squared
	dT_sq /= 128;
	dT_sq /= 128;	// looking for (dT/128) * (dT/128)
	if (dT >= 0) {
		 dT_sq /= 8;
	} else {
		 dT_sq /= 2;
	}
	// correct dT using second-order differential
	return dT - dT_sq;
	return 0;
}

int16_t get_temperature(int16_t dT, coeff_t* coeffs)
{
	int32_t acc = dT;
	acc *= (coeffs->TEMPSENS + 100);
	acc /= 2048;
	acc += 200;
	return (int16_t)acc;
}

uint16_t get_pressure(int16_t dT, coeff_t* coeffs)
{
	int16_t off;	// offset at temperature
	int32_t sens;	// sensitivity at temperature
	uint16_t P;		// pressure value
	int32_t pressure;	// actual pressure in mbar
	
	off = coeffs->TCO - 250;
	off *= dT;		// this shouldn't overflow, but watch out.
	off /= 4096;
	off += coeffs->OFFT1 + 10000;
	
	sens = coeffs->TCS + 200;
	sens *= dT;
	sens /= 8192;
	sens += coeffs->SENST1 / 2 + 3000;
	send_bytes(READ_D1);
	delay_ms(33);
	P =  get_bytes();
	pressure = P;
	pressure -= off;
	pressure *= sens;
	pressure /= 2048;
	pressure += 1000;
	return (uint16_t)pressure;
}

void init_SPI_MS5541(void){
		// set up SPI for emulating MS5541C's protocol
		SPI_DDR &= ~(_BV(SPI_MOSI)|_BV(SPI_MISO)|_BV(SPI_SS)|_BV(SPI_SCK));
		// Define the following pins as output
		SPI_DDR |= (_BV(SPI_MOSI) | _BV(SPI_SS) | _BV(SPI_SCK));
		SPI_PORT |= _BV(SPI_SS);
		SPCR = _BV(SPE) | _BV(MSTR) | _BV(SPR0);	// enable SPI, set as master, set prescaler to f_osc/16
}

void init_Timer0_MS5541(){
	
	DDRB |= (1 << PB3);		//set PB3=OC0A as output
	TIMSK0 = 0b00000000;	//disable all interrupts from Timer/Counter 0
	OCR0A = 0x3F;			//set OCR0A=63 => f_OC0A = 4190000/64/2 = 32768 Hz
	// configure Timer/Counter 0:
	//		COM0A = 0b01: Toggle OC0A on compare match
	//		COM0B = 0b00: OC0B disconnected
	//		WGM0 = 0b010: Clear Timer on compare match (CTC)
	//		CS0 = 0b001: clk_IO (no prescaling)
	TCCR0A = 0b01000010;
	TCCR0B = 0b00000001;
	
}


void init_MS5541(){
	
	init_Timer0_MS5541();
	init_SPI_MS5541();

	send_bytes(RESET);
	send_bytes(0);
}




